﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary
{
    /// <summary>
    /// This class provides a QR decomposition by using the Givens rotation method.
    /// </summary>
    [Serializable]
    public class GivensQRDecomposition
    {
        /// <summary>
        /// The matrix for the QR decomposition.
        /// </summary>
        private SquareMatrix sourceMatrix;

        /// <summary>
        /// Describes if the decomposition has a precision error.
        /// </summary>
        private bool precisionError;

        /// <summary>
        /// Initializes a new instance of the <see cref="GivensQRDecomposition"/> class.
        /// </summary>
        /// <param name="sourceMatrix">The source matrix for the QR decomposition.</param>
        public GivensQRDecomposition(SquareMatrix sourceMatrix)
        {
            this.precisionError = false;
            this.sourceMatrix = sourceMatrix;
        }

        /// <summary>
        /// Gets or sets the source matrix for the QR decomposition.
        /// </summary>
        /// <value>The source matrix for the QR decomposition.</value>
        public SquareMatrix SourceMatrix
        {
            get { return sourceMatrix; }
            set { sourceMatrix = value; }
        }

        /// <summary>
        /// Gets a value indicating whether the decomposition has a precision error.
        /// </summary>
        /// <value><c>True</c> if the decomposition has a precision error; otherwise, <c>false</c>.</value>
        public bool PrecisionError
        {
            get { return precisionError; }
        }

        /// <summary>
        /// This method provides a decomposition of the source matrix into n orthogonal, which may 
        /// used to transform the source matrix to a right triangular matrix by multiply the results
        /// with the source matrix.
        /// </summary>
        /// <param name="precision">The precision of the decomposition.</param>
        /// <param name="maxIterations">The maximum iterations of the decomposition.</param>
        /// <returns>An array of Givens rotation matrices Q1...Qn. If the matrices Q1...Qn will
        /// be multiply with the source matrix, the result will be a right triangular matrix.</returns>
        public GivensRotationMatrix[] ExecuteDecomposition(double precision, int maxIterations)
        {
            bool run = true;
            int iterationCounter = 0;
            List<GivensRotationMatrix> result = new List<GivensRotationMatrix>();
            Matrix workingMatrix = this.sourceMatrix.Copy();

            while ((iterationCounter < maxIterations) && run)
            {
                run = false;

                for (int columns = 0; columns < workingMatrix.Columns; columns++)
                {
                    for (int rows = columns + 1; rows < workingMatrix.Rows; rows++)
                    {
                        if (Math.Abs(workingMatrix.GetValueAtPosition(rows, columns)) > precision)
                        {
                            GivensRotationMatrix tempuri = GivensRotationMatrix.CreateFromMatrix(rows,
                                                                                                 columns,
                                                                                                 ref workingMatrix);

                            result.Add(tempuri);
                            workingMatrix *= tempuri;

                            if (Math.Abs(workingMatrix.GetValueAtPosition(rows, columns)) > precision)
                            {
                                run = true;
                            }
                        }
                    }
                }

                iterationCounter++;
            }

            if (iterationCounter < maxIterations)
            {
                this.precisionError = false;
            }
            else
            {
                this.precisionError = true;
            }

            return result.ToArray();
        }
    }
}